package binarization;

import draw.ArrayFromImage;
import draw.ExploringMedics;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;

public class AdaptiveBinary {

    BufferedImage image = null;
    int binaryImage[][];
    int imageDescription[][] = null;

    public AdaptiveBinary() {
    }

    public void setImage(int[][] imageDescription) {
        this.imageDescription = imageDescription;
        adaptiveBinary(imageDescription);
    }

    
    public void setImage(BufferedImage image) {
        this.image = image;
        adaptiveBinary(image);
    }

    public int[][] getBinaryImage() {
        return binaryImage;
    }

    public void adaptiveBinary(BufferedImage src) {
        ArrayFromImage array = new ArrayFromImage(src);
        imageDescription = array.getImageDescription();
        adaptiveBinary(src);
    }

    private int invertIntegralImage[][];
    private int invertIntegralSquareImage[][];

    private void countInvertIntegImage(int[][] imageDescription) {

        int width = imageDescription.length;
        int height = imageDescription[0].length;
        
        int white = 255;
        int black = 0;

        invertIntegralImage = new int[width][height];
        invertIntegralSquareImage = new int[width][height];

        invertIntegralImage[0][0] = imageDescription[0][0];
        invertIntegralSquareImage[0][0] = imageDescription[0][0] * imageDescription[0][0];

        for (int i = 1; i < width; i++) {
            
            int temp = white - imageDescription[i][0];
            invertIntegralImage[i][0] = invertIntegralImage[i - 1][0] + temp;
            invertIntegralSquareImage[i][0] = invertIntegralSquareImage[i - 1][0]
                    + temp * temp;
        }

        for (int j = 1; j < height; j++) {

            int temp = white - imageDescription[0][j];
            invertIntegralImage[0][j] = invertIntegralImage[0][j - 1] + temp;
            invertIntegralSquareImage[0][j] = invertIntegralSquareImage[0][j - 1]
                    + temp * temp;
        }

        for (int i = 1; i < width; i++) {
            for (int j = 1; j < height; j++) {

                int temp = white - imageDescription[i][j];
                invertIntegralImage[i][j] = invertIntegralImage[i][j - 1] + invertIntegralImage[i - 1][j]
                        - invertIntegralImage[i - 1][j - 1] + temp;
                invertIntegralSquareImage[i][j] = invertIntegralSquareImage[i - 1][j] + invertIntegralSquareImage[i][j - 1]
                        - invertIntegralSquareImage[i - 1][j - 1] + temp * temp;
            }
        }
    }
    
    private void countInvertIntegImage(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        int white = 255;
        int black = 0;

        invertIntegralImage = new int[width][height];
        invertIntegralSquareImage = new int[width][height];

        Raster raster = image.getRaster();
        int pixel[] = new int[4];
        int pixelPos = 1;
        raster.getPixel(0, 0, pixel);

        invertIntegralImage[0][0] = pixel[pixelPos];
        invertIntegralSquareImage[0][0] = pixel[pixelPos] * pixel[pixelPos];

        for (int i = 1; i < width; i++) {
            raster.getPixel(i, 0, pixel);
            int temp = white - pixel[pixelPos];
            invertIntegralImage[i][0] = invertIntegralImage[i - 1][0] + temp;
            invertIntegralSquareImage[i][0] = invertIntegralSquareImage[i - 1][0]
                    + temp * temp;
        }

        for (int j = 1; j < height; j++) {
            raster.getPixel(0, j, pixel);
            int temp = white - pixel[pixelPos];
            invertIntegralImage[0][j] = invertIntegralImage[0][j - 1] + temp;
            invertIntegralSquareImage[0][j] = invertIntegralSquareImage[0][j - 1]
                    + temp * temp;
        }

        for (int i = 1; i < width; i++) {
            for (int j = 1; j < height; j++) {
                raster.getPixel(i, j, pixel);
                int temp = white - pixel[pixelPos];
                invertIntegralImage[i][j] = invertIntegralImage[i][j - 1] + invertIntegralImage[i - 1][j]
                        - invertIntegralImage[i - 1][j - 1] + temp;
                invertIntegralSquareImage[i][j] = invertIntegralSquareImage[i - 1][j] + invertIntegralSquareImage[i][j - 1]
                        - invertIntegralSquareImage[i - 1][j - 1] + temp * temp;
            }
        }
    }

    
    private void adaptiveBinary(int[][] imageDescription) {
        int black = 0;
        int white = 255;
        final int R = 128;
        final double k = 0.15;
        final int w = 15;

        int width = imageDescription.length;
        int height = imageDescription[0].length;

        binaryImage = new int[width][height];

        if (invertIntegralImage == null || invertIntegralSquareImage == null) {
            countInvertIntegImage(imageDescription);
        }

    
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {

                int pixelVal = imageDescription[i][j];

                int minx = Math.min(i + (int) w / 2 + 1, width - 1);
                int miny = Math.min(j + (int) w / 2 + 1, height - 1);
                int maxx = Math.max(i - (int) w / 2, 0);
                int maxy = Math.max(j - (int) w / 2, 0);

                int m = (invertIntegralImage[minx][miny]
                        + invertIntegralImage[maxx][maxy]
                        - invertIntegralImage[minx][maxy]
                        - invertIntegralImage[maxx][miny]) / (w * w);

                double s = 1.0
                        * ((invertIntegralSquareImage[minx][miny]
                        + invertIntegralSquareImage[maxx][maxy]
                        - invertIntegralSquareImage[minx][maxy]
                        - invertIntegralSquareImage[maxx][miny]) / (w * w) - m * m);

                s = Math.pow(s, 0.5);

                int threshold = (int) (m * (1 + k * (s / R - 1)));
                int temp = 255 - pixelVal;
                int newVal = temp > threshold ? black : white;
                binaryImage[i][j] = newVal > 0 ? ExploringMedics.WHITE : ExploringMedics.BLACK;
            }
        }
    }
}
